/*******************************************************************************
 * Copyright (c) 2009 IBM Corporation and others
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.aseclipse.osgi.classes {
    
    import org.aseclipse.core.runtime.registry.classes.BundleModel;
    import org.aseclipse.osgi.Bundle;
    import org.aseclipse.osgi.BundleActivator;
    import org.aseclipse.osgi.BundleContext;
    import org.aseclipse.osgi.BundleEvent;
    import org.aseclipse.osgi.loader.BundleConstants;
    import org.aseclipse.osgi.loader.BundleLibraryLoader;
    import org.aseclipse.osgi.loader.BundleLocaleLoader;
    import org.aseclipse.osgi.loader.BundleThemeLoader;
    
    import flash.utils.getDefinitionByName;
    
    import mx.utils.StringUtil;
    

public class BundleImpl extends Bundle { // EventDispatcher implements
    
    // loading theme, locale and library
	private var isLibraryLoading:Boolean = false;
    private var isLibraryLoaded:Boolean = false;
    
	private var singleton:Boolean = false;
	private var markedStarted:Boolean = false;
    
	private var state:int;
	private var framework:BundleFrameworkImpl;
	protected var bundleContext:BundleContext;

    // bundle meta data: plugin.xml
    private var bundleData:BundleModel;	
	// bundle activator instance from plugin.xml
	private var activator:Object;
    
    /****
    * A state of required bundles are resolving
    * When BundleResolveService want to resolve a bundle, it will check the bundle 
    * contains required bundles. If exist, need to resolve them once. The flag indicates
    * the phase of resolving required bundles.
    */ 
    public var requiresResolving:Boolean = false;
	
	public var requiresStarting:Boolean =  false;
	
	public var extendersLoading:Boolean =  false;
	public var extendersReady:Boolean =  false;
	
	public function BundleImpl(framework:BundleFrameworkImpl, bundleData:BundleModel) {
		this.framework = framework;
		this.bundleData = bundleData;
		bundleContext = framework.getBundleContext(this);
		state = INSTALLED;
	}
    
    /******
    * Notify others for installed status at BundleInstallService
    * Only invoked when a bundle is installed (loading its plugin.xml)
    */ 
    public function notifyInstalled():void {
        framework.getBundleDispatcher().dispatchEvent(new BundleEvent(BundleEvent.INSTALLED, this));
    }
    /***
    * Only invoked when a bundle's extenders are installed (loading its plugin.xml)
    */ 
    public function notifyExtendersInstalled():void {
        framework.getBundleDispatcher().dispatchEvent(new BundleEvent(BundleEvent.EXTENDERS_INSTALLED, this));
    }
    
	/* (non-Javadoc)
	 * @see org.eclipse.e4.internal.javascript.JSBundle#getSymbolicName()
	 */
	override public function getSymbolicName():String {
		return bundleData.getUniqueIdentifier();
	}
    
    // Get all extension bundles
    public function getExtenderPlugins():Array {
        return new Array();
    }
    
    public function hasRequires():Boolean {
        return bundleData.getRequirePlugins().length > 0;
    }
    
    public function hasExtensionPoint():Boolean {
        return bundleData.getExtensionPoints().length > 0;
    }
    
    public function getPluginModel():BundleModel {
        return bundleData;
    }
	/* (non-Javadoc)
	 * @see org.eclipse.e4.internal.javascript.JSBundle#getVersion()
	 */
	override public function getVersion():String {
		return bundleData.getVersion();
	}
    
    public function getActivatorClass():String {
        return bundleData.getPluginActivator();
    }
    public function getLibraries():Array {
        return bundleData.getLibraries();
    }
    
	override public function getRequires():Array {
		return bundleData.getRequirePlugins();
	}

	override public function getBundleId():int {
		return bundleData.getId();
	}

	override public function getState():int {
		return state;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.e4.internal.javascript.JSBundle#uninstall()
	 */
	override public function uninstall():void {
		stop();
		state = UNINSTALLED;
		try {
		    bundleData.destroy();
		} catch(er:Error) {
		    
		}
	}

    /***
    * Only resolve once when finishing installation, loading its swc binary
    * Invoked by BundleResolveService
    */ 
	public function resolve():void {
		if (state != INSTALLED)
			return;
        if(isLibraryLoading == true || isLibraryLoaded == true) {
            return;
        }
        isLibraryLoading = true;
        // start to load library
        var array:Array = getLibraries();
        if(array.length == 0 ) {
            loadingLocale();
        } else {
            var urlList:String = "";
            urlList = getLibrariesURL(array);
            BundleLibraryLoader.getInstance().load(urlList, loadingLocale, loadingLocale);
        }
	}
	
	/***
	 * Start to load locale if library is loaded
	 */ 
	private function loadingLocale():void {
	    var array:Array = bundleData.getLocales();
	    if(array.length == 0 ) {
            loadingTheme();
        } else {
            var urlList:String = "";
            urlList = getGeneralURL(array);
            BundleLocaleLoader.getInstance().loadResources(urlList.split(";"), loadingTheme);
        }
	}
	/***
	 * Start to load theme if locale is loaded
	 */ 
	private function loadingTheme():void {
	    var array:Array = bundleData.getStyles();
	    if(array.length == 0 ) {
            loadingFinished();
        } else {
            var urlList:String = "";
            urlList = getGeneralURL(array);
            BundleThemeLoader.getInstance().load(urlList, loadingFinished);
        }
	}
	
	/***
	 * Start to notify if bundle is resolved
	 */ 	
    private function loadingFinished():void {
        isLibraryLoading = false;
        isLibraryLoaded = true;
        state = RESOLVED;
		framework.getBundleDispatcher().dispatchEvent(new BundleEvent(BundleEvent.RESOLVED, this));
    }
    
    private function loadingFailed(event:Object):void {
        // TODO 
        trace(event);
    }
    	
    private function getLibrariesURL(array:Array):String {
        var str:String = "";
        for each(var obj:Object in array) {
            var item:String = obj.url;
            if(obj.isInternal == true) {
                // library is from current plugin resource
                var itemStr:String = BundleConstants.getResourceURL(getSymbolicName(), item);
                str = str + itemStr + ";";
            } else {
                // library is from internet
                str = str + item + ";";
            }
        }
        str = str.substring(0, str.length - 1);
        return str;
    }
    
    private function getGeneralURL(array:Array):String {
        var str:String = "";
        for each(var obj:Object in array) {
            var item:String = obj.url;
            if(obj.isInternal == true) {
                var itemStr:String = BundleConstants.getResourceURL(getSymbolicName(), item);
                str = str + itemStr + ";";
            } else {
                str = str + item + ";";
            }
        }
        str = str.substring(0, str.length - 1);
        return str;
    }
    
    private function enrichURL(array:Array):String {
        var str:String = "";
        for each(var obj:Object in array) {
            var item:String = obj.url;
            var itemStr:String = BundleConstants.getResourceURL(getSymbolicName(), item);
            str = str + itemStr + ";";
        }
        str = str.substring(0, str.length - 1);
        return str;
    }
        
	protected function unresolve():void {
		if (state == ACTIVE) {
			stop();
			markedStarted = true;
		}
		//scope = null;

		if (state == RESOLVED)
			state = INSTALLED;
	}

	public function toString():String {
		return "JavaScriptBundle{symbolic-name=" + getSymbolicName() + ", version=" + getVersion() + "}"; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
	}

	/*****************
	 * initialize activiator if exist, and invoke its "start()" method
	 * 
	 */
	override public function start():void {
		markedStarted = true;
		if (state != RESOLVED)
			return;

		state = STARTING;
		
		var clazz:String = getActivatorClass();
		if(clazz != null && StringUtil.trim(clazz) != "") {
    		var pluginClaz:Class;
    		try {
    		    if(clazz != null)
    		        pluginClaz = getDefinitionByName(clazz) as Class;
    		} catch(er:Error) {
    		    throw new Error("Can't initialize activator "+clazz);
    		}
    		activator = new pluginClaz();
    		if(!(activator is BundleActivator)) {
    		    throw new Error(clazz + " is not a subclass of BundleActivator");
    		}
    		// synchorous start bundle
    		try {
    		    (activator as BundleActivator).start(bundleContext);
    		} catch(e:Error) {
    		    throw new Error("Fail to start activator "+clazz + ", " + e.getStackTrace());
    		}
		} else {
		    trace("No bundle activator for " + this.getSymbolicName());
		}
		state = ACTIVE;
        // notify others
        framework.getBundleDispatcher().dispatchEvent(new BundleEvent(BundleEvent.STARTED, this));	
	}

	override public function stop():void {
		markedStarted = false;
		if (state != ACTIVE)
			return;

		state = STOPPING;
		if(activator)
		    activator.stop(bundleContext);
		state = RESOLVED;
	}

	public function isSingleton():Boolean {
		return singleton;
	}

	public function isMarkedStarted():Boolean {
		return markedStarted;
	}

}
}
